// ==UserScript== // @name TradingView Alert Batch Creator // @namespace none // @version 1.0 // @description 在tradingview图表界面批量添加警报 // @author Elias // @grant GM_setValue // @grant GM_getValue // @match https://*.tradingview.com/chart* // @require https://scriptcat.org/lib/637/1.3.3/ajaxHooker.js // ==/UserScript== (function () { 'use strict'; var log_username = GM_getValue('log_username', null); var build_time = GM_getValue('build_time', null); var maintenance_unset_reason = GM_getValue('maintenance_unset_reason', null) var messageBoxRef; // 设置过滤规则,只劫持到特定URL的POST请求 ajaxHooker.filter([ { url: 'https://pricealerts.tradingview.com/create_alert', // 只劫持到这个URL的请求 method: 'POST', // 只劫持POST请求 }, ]); // 获取相关参数 ajaxHooker.hook(request => { // 检查是否为创建警报的请求 if (request.url.includes('create_alert')) { const urlParams = new URLSearchParams(request.url.split('?')[1]); const log_username = urlParams.get('log_username'); const build_time = urlParams.get('build_time'); const maintenance_unset_reason = urlParams.get('maintenance_unset_reason'); // 存储提取的参数,以便后续使用 GM_setValue('log_username', log_username); GM_setValue('build_time', build_time); GM_setValue('maintenance_unset_reason', maintenance_unset_reason); console.log(`Captured: ${log_username}, ${build_time}, ${maintenance_unset_reason}`); } }); // 创建警报的函数 const createAlert = ({ exchange, crypto, price, resolution = "5", onComplete }) => { // 获取当前日期 const currentDate = new Date(); // 计算下个月的日期,Date对象会自动处理月份和年份的变化 currentDate.setMonth(currentDate.getMonth() + 1); // 将日期转换为ISO格式字符串,并截取前面的日期部分 const expiration = currentDate.toISOString(); const apiUrl = `https://pricealerts.tradingview.com/create_alert?log_username=${log_username}&build_time=${build_time}&maintenance_unset_reason=${maintenance_unset_reason}`; const symbolValue = `={"adjustment":"splits","currency-id":"XTVCUSDT","session":"regular","symbol":"${exchange}:${crypto}"}`; const payload = JSON.stringify({ "payload": { "symbol": symbolValue, "resolution": resolution, "message": `${crypto} 穿过(Crossing) ${price}`, "sound_file": "alert/fired", "sound_duration": 0, "popup": true, "expiration": expiration, "condition": { "type": "cross", "frequency": "on_first_fire", "series": [{ "type": "barset" }, { "type": "value", "value": price }] }, "auto_deactivate": true, "email": true, "sms_over_email": false, "mobile_push": true, "web_hook": null, "name": null, "active": true, "ignore_warnings": true, } }); fetch(apiUrl, { method: "POST", headers: { "content-type": "text/plain;charset=UTF-8", "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-site", "x-usenewauth": "true" }, body: payload, referrer: "https://cn.tradingview.com/", referrerPolicy: "origin-when-cross-origin", mode: "cors", credentials: "include" }) .then(response => response.json()) .then(data => { console.log(data); if (data.s !== 'ok') { // 如果状态不是"ok",则认为添加失败,提示用户 if (typeof onComplete === 'function') { onComplete(false, "添加警报失败,请检查是否需要更新参数或手动添加警报。"); } } else { // 如果状态是"ok",则认为添加成功 if (typeof onComplete === 'function') { onComplete(true, `${crypto} 警报添加成功`); } } }) .catch(error => { console.error('Error:', error); if (typeof onComplete === 'function') { onComplete(false, `请求错误:${error.message}`); } }); }; // 创建UI元素 function createUI() { // 创建一个挂起按钮 const toggleButton = document.createElement('button'); toggleButton.textContent = '添加警报'; toggleButton.style.position = 'fixed'; toggleButton.style.right = '10px'; toggleButton.style.bottom = '200px'; // 或者选择一个合适的位置 toggleButton.style.zIndex = '10001'; // 确保挂起按钮的zIndex高于其他UI元素 // 创建一个容器 const container = document.createElement('div'); container.style.position = 'fixed'; container.style.bottom = '10px'; container.style.right = '10px'; container.style.padding = '10px'; container.style.backgroundColor = 'white'; container.style.border = '1px solid black'; container.style.zIndex = '10000'; container.style.width = '400px'; container.style.height = 'auto'; container.style.display = 'none'; // 默认隐藏容器 // 创建一个用于显示消息的元素 messageBoxRef = document.createElement('div'); messageBoxRef.style.padding = '5px'; messageBoxRef.style.marginTop = '10px'; messageBoxRef.style.textAlign = 'center'; messageBoxRef.style.display = 'none'; // 默认隐藏 container.appendChild(messageBoxRef); // 创建一个文本域用于输入数据 const textArea = document.createElement('textarea'); textArea.placeholder = "输入数据,格式为:\nSYMBOL,PRICE\n例如:\nBTCUSDT.P,71000"; textArea.style.width = '100%'; textArea.style.height = '150px'; container.appendChild(textArea); // 创建一个提交按钮 const submitButton = document.createElement('button'); submitButton.textContent = '提交'; submitButton.style.display = 'block'; submitButton.style.marginTop = '10px'; submitButton.onclick = function () { // 处理文本域中的数据 const data = textArea.value; processData(data); }; container.appendChild(submitButton); // 切换容器添加警报/隐藏的函数 toggleButton.onclick = function () { if (container.style.display === 'none') { container.style.display = 'block'; toggleButton.textContent = '隐藏'; } else { container.style.display = 'none'; toggleButton.textContent = '添加警报'; } }; // 将容器和挂起按钮添加到body document.body.appendChild(container); document.body.appendChild(toggleButton); } // 创建一个函数来更新消息框的状态和文本 function showMessage(element, message, success) { element.textContent = message; element.style.display = 'block'; element.style.color = success ? 'green' : 'red'; // 成功为绿色,失败为红色 setTimeout(() => { element.style.display = 'none'; }, 5000); // 5秒后隐藏消息 } // 处理数据的函数 function processData(data) { // 尝试从存储中获取参数 log_username = GM_getValue('log_username', null); build_time = GM_getValue('build_time', null); maintenance_unset_reason = GM_getValue('maintenance_unset_reason', null); // 检查是否所有必要的参数都已经获取 if (!log_username || !build_time || !maintenance_unset_reason) { // 如果没有找到参数,则弹出提示 alert("请先手动添加一个警报以便脚本捕获必要的参数。"); return } // 参数正常,继续运行 const lines = data.split('\n'); lines.forEach(line => { const [crypto, price] = line.split(','); if (crypto && price) { console.log(`创建警报:${crypto} - ${price}`); createAlert({ exchange: "BINANCE", crypto: crypto, price: parseFloat(price), onComplete: (success, message) => { showMessage(messageBoxRef, message, success); } }); } }); } // 调用createUI函数以创建UI createUI(); })();